﻿/*
Copyright © 2011 François Karman

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

See <license.txt> for the complete license of the software.
*/

using System;
using System.IO;
using System.Runtime.Serialization;

namespace Simple.Storage.Serializer
{
    /// <summary>
    /// Base class for the simpliest serializers - those that are associated to a specific type.
    /// </summary>
    public abstract class AbstractMonoTypeSerializer : AbstractSerializer
    {
        /// <summary>
        /// Gets the type of elements managed by the serializer.
        /// </summary>
        protected abstract Type AssociatedType { get; }

        /// <summary>
        /// Checks if the serializer is associated to a specific type.
        /// </summary>
        /// <param name="type">The checked type.</param>
        /// <returns>
        /// <c>true</c> if the serializer can work with instances of <paramref name="type"/>;
        /// <c>false</c> otherwise.
        /// </returns>
        /// <seealso cref="AbstractMonoTypeSerializer.AssociatedType"/>
        protected sealed override bool InternalCanSerialize(Type type)
        {
            return this.AssociatedType.IsAssignableFrom(type);
        }

        /// <summary>
        /// Checks whether the <see cref="ISerializer.Deserialize(BinaryReader, Type, object)"/>*
        /// method is implemented by this serializer.
        /// </summary>
        /// <param name="type">The checked type.</param>
        /// <returns>
        /// Always <c>false</c>.
        /// </returns>
        protected override bool InternalCanWorkInPlace(Type type)
        {
            return false;
        }

        /// <summary>
        /// Implements the serialization logic.
        /// </summary>
        /// <param name="writer">
        /// The writer - guaranty to be not <c>null</c>.
        /// </param>
        /// <param name="type">
        /// The reference type - guaranty to be not <c>null</c>.
        /// </param>
        /// <param name="value">
        /// The value to serialize - guaranty to be not <c>null</c> and associated with
        /// the current serializer.
        /// </param>
        protected sealed override void InternalSerialize(BinaryWriter writer, Type type, object value)
        {
            if (type != this.AssociatedType)
            {
                string message = string.Format(
                    Messages.Culture,
                    Messages.SerializerWrongType,
                    this.AssociatedType.FullName,
                    type.FullName);
                throw new SerializationException(message);
            }

            this.InternalSerialize(writer, value);
        }

        /// <summary>
        /// Implements the deserialization logic.
        /// </summary>
        /// <param name="reader">
        /// The reader - guaranty to be not <c>null</c>.
        /// </param>
        /// <param name="type">
        /// The reference type - guaranty to be not <c>null</c>.
        /// </param>
        /// <returns>The deserialized value.</returns>
        protected override object InternalDeserialize(BinaryReader reader, Type type)
        {
            if (type != this.AssociatedType)
            {
                string message = string.Format(
                    Messages.Culture,
                    Messages.SerializerWrongType,
                    this.AssociatedType.FullName,
                    type.FullName);
                throw new SerializationException(message);
            }

            return this.InternalDeserialize(reader);
        }

        /// <summary>
        /// Implements the in-place deserialization logic.
        /// </summary>
        /// <param name="reader">
        /// The reader - guaranty to be not <c>null</c>.
        /// </param>
        /// <param name="type">
        /// The reference type - guaranty to be not <c>null</c>.
        /// </param>
        /// <param name="value">
        /// The contener for the deserialized object - guaranty to be not <c>null</c>.
        /// </param>
        protected override void InternalDeserialize(BinaryReader reader, Type type, object value)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Implements the serialization logic for the type associated with this serializer.
        /// </summary>
        /// <param name="writer">
        /// The writer - guaranty to be not <c>null</c>.
        /// </param>
        /// <param name="value">
        /// The value to serialize - guaranty to be not <c>null</c> and associated with
        /// the current serializer.
        /// </param>
        protected abstract void InternalSerialize(BinaryWriter writer, object value);

        /// <summary>
        /// Implements the deserialization logic for the type associated with this serializer.
        /// </summary>
        /// <param name="reader">
        /// The reader - guaranty to be not <c>null</c>.
        /// </param>
        /// <returns>The deserialized value.</returns>
        protected abstract object InternalDeserialize(BinaryReader reader);
    }
}
